﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Quality
{
   public  class SixSigmaHelper
    {

        public const double d2 = 1.128;
        public const double d3 = 1.693;
        public const double d4 = 2.059;
        public const double d5 = 2.326;
        /// <summary>
        /// 数组求和
        /// </summary>
        /// <param name="originalData">原始数据数组</param>
        /// <returns>返回数组的和</returns>
        public static double GetSumValue(double[] originalData)
        {
            double sum = 0.0;
            for (int i = 0; i < originalData.Length; i++)
            {
                sum = sum + originalData[i];
            }
            return DoubleFormat(sum);
        }

        /// <summary>
        /// 数组求平均数
        /// </summary>
        /// <param name="originalData">原始数据数组</param>
        /// <returns>返回数组的平均值</returns>
        public static double GetAverageValue(double[] originalData)
        {
            double sum = 0.0;
            double ave = 0.0;
            for (int i = 0; i < originalData.Length; i++)
            {
                sum = sum + originalData[i];
            }
            ave = sum / originalData.Length;
            return DoubleFormat(ave);
        }
        /// <summary>
        /// 按指定小数精度计算数组平均值
        /// </summary>
        /// <param name="digits">精度，即保留的小数位数</param>
        /// <param name="originalData"原始数据>原始数据数组</param>
        /// <returns>返回指定小数精度的平均值</returns>
        public static double GetAverageValue(int digits, double[] originalData)
        {
            double sum = 0.0;
            double ave = 0.0;
            for (int i = 0; i < originalData.Length; i++)
            {
                sum = sum + originalData[i];
            }
            ave = sum / originalData.Length;
            return DoubleFormat(digits, ave);
        }
        /// <summary>
        /// 获取整体平均值：先分组，计算组内平均值，然后计算整体平均值
        /// </summary>
        /// <param name="digits">精度，小数位数</param>
        /// <param name="groupSize">组大小</param>
        /// <param name="originalData"> 原始数据</param>
        /// <returns>返回整体平均值</returns>
        public static double GetTotalAveByGroup(int digits, int groupSize, double[] originalData)
        {
            double[] groupAve = GetGroupAve(digits, groupSize, originalData);
            return GetAverageValue(digits, groupAve);
        }
        /// <summary>
        /// 获取子组平均数，以数组形式返回
        /// </summary>
        /// <param name="digits">精度，小数位数</param>
        /// <param name="groupSize">组大小</param>
        /// <param name="originalData">原始数据</param>
        /// <returns>返回子组平均值</returns>
        public static double[] GetGroupAve(int digits, int groupSize, double[] originalData)
        {
            double groupNum = Math.Ceiling(Convert.ToDouble(originalData.Length / groupSize));
            double[] groupAve = new double[Convert.ToInt32(groupNum)];
            for (int i = 0; i < Convert.ToInt32(groupNum); i++)
            {
                int index = i * groupSize;
                double[] temp = new double[groupSize];
                for (int j = 0; j < groupSize; j++)
                {
                    temp[j] = originalData[index + j];
                }
                groupAve[i] = GetAverageValue(digits, temp);
            }
            return groupAve;
        }
        /// <summary>
        /// 求整体标准差
        /// </summary>
        /// <param name="originalData">原始数据</param>
        /// <returns>返回标准差</returns>
        public static double GetTotalStdDev(double[] originalData)
        {
            double ave = 0.0;
            double stdDev = 0.0;
            if (originalData.Length == 1 && originalData[0] == 0.0)
            {
                return stdDev = 0.0;
            }
            ave = GetAverageValue(originalData);
            for (int i = 0; i < originalData.Length; i++)
            {
                stdDev = (double)(stdDev + Math.Pow((originalData[i] - ave), 2));
            }
            stdDev = (double)Math.Sqrt(stdDev / (originalData.Length - 1));
            return DoubleFormat(stdDev);
        }
        /// <summary>
        /// 按指定小数精度获取标准差
        /// </summary>
        /// <param name="digits">精度，即小数位数</param>
        /// <param name="originalData">原始数据</param>
        /// <returns>返回标准差</returns>
        public static double GetStdDev(int digits, double[] originalData)
        {
            double ave = 0.0;
            double stdDev = 0.0;
            ave = GetAverageValue(originalData);
            for (int i = 0; i < originalData.Length; i++)
            {
                stdDev = (double)(stdDev + Math.Pow((originalData[i] - ave), 2));
            }
            stdDev = (double)Math.Sqrt(stdDev / (originalData.Length - 1));
            return DoubleFormat(digits, stdDev);
        }

        /// <summary>
        /// 求各组移动极差均值
        /// </summary>
        /// <param name="size">子组容量</param>
        /// <param name="originalData">原始数据</param>
        /// <returns>返回移动极差均值</returns>
        public static double[] GetGroupMoveRangeMean(int size, double[] originalData)
        {
            
            double[] range = new double[originalData.Length - (size - 1)];
            for (int i = (size - 1); i < originalData.Length; i++)
            {
                double[] temp = new double[size];
                int count = size;
                int idx = 0;
                do
                {
                    temp[idx] = originalData[i - (count - 1)];
                    idx++;
                    count--;
                } while ((count > 0));
                range[i - (size - 1)] = (double)Math.Abs(GetArrMax(temp) - GetArrMin(temp));
            }
           
           
            return range;
        }
        /// <summary>
        /// 求移动极差均值
        /// </summary>
        /// <param name="size">子组容量</param>
        /// <param name="originalData">原始数据</param>
        /// <returns>返回移动极差均值</returns>
        public static double GetMoveRangeMean(int size, double[] originalData)
        {
            double moveSum = 0.0;
            double moveAve = 0.0;
            double[] range = new double[originalData.Length - (size - 1)];
            for (int i = (size - 1); i < originalData.Length; i++)
            {
                double[] temp = new double[size];
                int count = size;
                int idx = 0;
                do
                {
                    temp[idx] = originalData[i - (count - 1)];
                    idx++;
                    count--;
                } while ((count > 0));
                range[i - (size - 1)] = (double)Math.Abs(GetArrMax(temp) - GetArrMin(temp));
            }
            for (int i = 0; i < range.Length; i++)
            {
                moveSum = moveSum + range[i];
            }
            moveAve = moveSum / range.Length;
            return DoubleFormat(moveAve);
        }

        /// <summary>
        /// 求组内标准差
        /// </summary>
        /// <param name="size">子组容量</param>
        /// <param name="originalData">原始数据</param>
        /// <returns>返回组内标准差</returns>
        public static double GetInGroupStdDev(int size, double[] originalData)
        {
            double gourpStdDev = 0.0;
            double moveAve = GetMoveRangeMean(size, originalData);
            switch (size)
            {
                case 2:
                    gourpStdDev = (double)(moveAve / d2);
                    break;
                case 3:
                    gourpStdDev = (double)(moveAve / d3);
                    break;
                case 4:
                    gourpStdDev = (double)(moveAve / d4);
                    break;
                case 5:
                    gourpStdDev = (double)(moveAve / d5);
                    break;
                default:
                    gourpStdDev = (double)(moveAve / d2);
                    break;
            }
            return DoubleFormat(gourpStdDev);
        }

        /// <summary>
        /// 获取整体正态分布曲线数组值
        /// </summary>
        /// <param name="target">望目</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回整体正态分布曲线数组值</returns>
        public static Dictionary<string, object> GetNormalDistribution(double target, double[] original)
        {
            Dictionary<string, object> json = new Dictionary<string, object>();
            double sigma = GetTotalStdDev(original);
            double lcl = DoubleFormat(target - 3 * sigma);
            double ucl = DoubleFormat(target + 3 * sigma);
            int idx = 10;
            double plus = 0.0;
            plus = ((double)((int)(ucl - lcl + 1))) / 10;
            double[] categories = new double[idx];
            double[] values = new double[idx];
            categories[0] = lcl;
            for (int i = 1; i < idx; i++)
            {
                categories[i] = DoubleFormat(categories[i - 1] + plus);
            }
            for (int i = 0; i < idx; i++)
            {
                values[i] = 0.0;
            }
            for (int i = 1; i < categories.Length; i++)
            {
                values[i] = DoubleFormat(1
                        / ((Math.Sqrt(2 * Math.PI) * GetTotalStdDev(original)))
                        * Math.Pow(
                                Math.E,
                                -Math.Pow((categories[i] - GetAverageValue(original)), 2)
                                        / (2 * Math
                                                .Pow(GetTotalStdDev(original), 2))));
            }
            json.Add("categories", categories);
            json.Add("values", values);
            return json;
        }
        /// <summary>
        /// 获取组内正态分布曲线数组值
        /// </summary>
        /// <param name="target">望目</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回组内正态分布曲线数组值</returns>
        public static Dictionary<string, object> GetInGroupNormalDistribution(double target, double[] original)
        {
            Dictionary<string, object> json = new Dictionary<string, object>();
            double sigma = GetTotalStdDev(original);
            double lcl = DoubleFormat(target - 3 * sigma);
            double ucl = DoubleFormat(target + 3 * sigma);
            int idx = 10;
            double plus = 0.0;
            plus = ((double)((int)(ucl - lcl + 1))) / 10;
            double[] categories = new double[idx];
            double[] values = new double[idx];
            double inGroupStdDev = GetInGroupStdDev(2, original);
            double ave = GetAverageValue(original);
            categories[0] = lcl;
            for (int i = 1; i < idx; i++)
            {
                categories[i] = DoubleFormat(categories[i - 1] + plus);
            }
            for (int i = 0; i < idx; i++)
            {
                values[i] = 0.000000;
            }
            for (int i = 1; i < categories.Length; i++)
            {
                values[i] = DoubleFormat(1
                       / ((Math.Sqrt(2 * Math.PI) * inGroupStdDev))
                       * Math.Pow(
                               Math.E,
                               -Math.Pow((categories[i] - ave), 2)
                                       / (2 * Math.Pow(inGroupStdDev, 2))));
            }
            json.Add("categories", categories);
            json.Add("values", values);
            return json;
        }
        /// <summary>
        /// 获取数据分布情况
        /// </summary>
        /// <param name="target">望目</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回数据分布情况</returns>
        public static Dictionary<string, object> GetDataDistribution(double target, double[] original)
        {
            Dictionary<string, object> json = new Dictionary<string, object>();
            double sigma = GetTotalStdDev(original);
            double lcl = DoubleFormat(target - 3 * sigma);
            double ucl = DoubleFormat(target + 3 * sigma);
            int idx = 10;
            double plus = 0.0;
            plus = ((double)((int)(ucl - lcl + 1))) / 10;
            double[] categories = new double[idx];
            double[] values = new double[idx];
            String[] test = new String[idx];
            categories[0] = lcl;
            for (int i = 1; i < idx; i++)
            {
                categories[i] = DoubleFormat(categories[i - 1] + plus);
            }
            for (int i = 0; i < idx; i++)
            {
                values[i] = 0.0;
                test[i] = "";
            }
            for (int i = 0; i < original.Length; i++)
            {
                for (int j = 0; j < categories.Length; j++)
                {
                    if (categories[j] - 0.5 < original[i] && original[i] <= categories[j] + 0.5)
                    {
                        values[j] += 1;
                        test[j] += original[i] + ",";
                    }
                }
            }
            for (int i = 0; i < idx; i++)
            {
                values[i] = values[i] / 100;
            }
            json.Add("categories", categories);
            json.Add("values", values);
            return json;
        }
        /// <summary>
        /// 无偏移过程能力指数
        /// </summary>
        /// <param name="upperT">公差上限</param>
        /// <param name="lowerT">公差下限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回无偏移过程能力指数</returns>
        public static double GetPP(double upperT, double lowerT, double[] original)
        {
            double pp = new double();
            double sigma = GetTotalStdDev(original);
            pp = (upperT - lowerT) / (6 * sigma);
            return DoubleFormat(pp);
        }
        /// <summary>
        /// 无偏移下单限过程能力指数
        /// </summary>
        /// <param name="lowerT">公差下限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回无偏移下单限过程能力指数</returns>
        public static double GetPPL(double lowerT, double[] original)
        {
            double ppl = new double();
            double sigma = GetTotalStdDev(original);
            double ave = GetAverageValue(original);
            ppl = (ave - lowerT) / (3 * sigma);
            return DoubleFormat(ppl);
        }
        /// <summary>
        /// 无偏移上单限过程能力指数
        /// </summary>
        /// <param name="upperT">公差上限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回无偏移上单限过程能力指数</returns>
        public static double GetPPU(double upperT, double[] original)
        {
            double ppl = new double();
            double sigma = GetTotalStdDev(original);
            double ave = GetAverageValue(original);
            ppl = (upperT - ave) / (3 * sigma);
            return DoubleFormat(ppl);
        }
        /// <summary>
        /// 有偏移过程能力指数
        /// </summary>
        /// <param name="upperT">公差上限</param>
        /// <param name="lowerT">公差下限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回有偏移过程能力指数</returns>
        public static double getPPK(double upperT, double lowerT, double[] original)
        {
            double ppl = GetPPL(lowerT, original);
            double ppu = GetPPU(upperT, original);
            return DoubleFormat(ppl > ppu ? ppu : ppl);
        }
        /// <summary>
        /// 无偏移短期过程能力指数
        /// </summary>
        /// <param name="upperT">公差上限</param>
        /// <param name="lowerT">公差下限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回无偏移短期过程能力指数</returns>
        public static double GetInGroupCP(double upperT, double lowerT, double[] original)
        {
            double cp = new double();
            double sigma = GetInGroupStdDev(2, original);
            if (sigma == 0)
            {
                cp = 0;
            }
            else
            {
                cp = (upperT - lowerT) / (6 * sigma);
            }

            return DoubleFormat(cp);
        }
        /// <summary>
        /// 无偏移下单限短期过程能力指数
        /// </summary>
        /// <param name="lowerT">公差下限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回无偏移下单限短期过程能力指数</returns>
        public static double GetInGroupCPL(double lowerT, double[] original)
        {
            double cpl = new double();
            double sigma = GetInGroupStdDev(2, original);
            if (sigma == 0)
            {
                return 0;
            }
            else
            {
                double ave = GetAverageValue(original);
                cpl = (ave - lowerT) / (3 * sigma);
                return DoubleFormat(cpl);
            }
        }
        /// <summary>
        /// 无偏移上单限短期过程能力指数
        /// </summary>
        /// <param name="upperT">公差上限</param>
        /// <param name="original">原始数据</param>
        /// <returns>反回无偏移上单限短期过程能力指数</returns>
        public static double GetInGroupCPU(double upperT, double[] original)
        {
            double cpu = new double();
            double sigma = GetInGroupStdDev(2, original);
            if (sigma == 0)
            {
                return 0;
            }
            else
            {
                double ave = GetAverageValue(original);
                cpu = (upperT - ave) / (3 * sigma);
                return DoubleFormat(cpu);
            }
        }
        /// <summary>
        /// 有偏移短期过程能力指数
        /// </summary>
        /// <param name="upperT">公差上限</param>
        /// <param name="lowerT">公差下限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回有偏移短期过程能力指数</returns>
        public static double GetInGroupCPK(double upperT, double lowerT, double[] original)
        {
            double cpl = GetInGroupCPL(lowerT, original);
            double cpu = GetInGroupCPU(upperT, original);
            return DoubleFormat(cpl > cpu ? cpu : cpl);
        }
        /// <summary>
        /// 自然上限
        /// </summary>
        /// <param name="target">望目</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回自然上限</returns>
        public static double GetUCL(double target, double[] original)
        {
            double sigma = GetTotalStdDev(original);
            return DoubleFormat(target + 3 * sigma);
        }
        /// <summary>
        /// 自然下限
        /// </summary>
        /// <param name="target">望目</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回自然下限</returns>
        public static double GetLCL(double target, double[] original)
        {
            double sigma = GetTotalStdDev(original);
            return DoubleFormat(target - 3 * sigma);
        }
        /// <summary>
        /// 最小值
        /// </summary>
        /// <param name="array">原始数据数组</param>
        /// <returns>返回最小值</returns>
        public static double GetArrMin(double[] array)
        {
            double min = array[0];
            for (int i = 0; i < array.Length; i++)
            {
                if (min > array[i])
                    min = array[i];
            }
            return min;
        }
        /// <summary>
        /// 最大值
        /// </summary>
        /// <param name="array">原始数据数组</param>
        /// <returns>返回最大值</returns>
        public static double GetArrMax(double[] array)
        {
            double max = array[0];
            for (int i = 0; i < array.Length; i++)
            {
                if (max < array[i])
                    max = array[i];
            }
            return max;
        }
        /// <summary>
        /// 数据格式化，默认保留5位小数
        /// </summary>
        /// <param name="d">要格式化的数据</param>
        /// <returns>返回格式化后的数据</returns>
        public static double DoubleFormat(double d)
        {
            double result = 0.0;
            if (double.TryParse(d.ToString(), out result))
            {
                decimal b = new decimal(d);
                double formatD = Convert.ToDouble(Math.Round(b, 5));
                //b.setScale(5, BigDecimal.ROUND_HALF_UP).doubleValue();
                return formatD;
            }
            else
            {
                return 0.0;
            }
        }
        /// <summary>
        /// 按指定精度进行数据格式化
        /// </summary>
        /// <param name="digits">精度，小数位数</param>
        /// <param name="d">要格式化的数据</param>
        /// <returns>返回格式化后的数据</returns>
        public static double DoubleFormat(int digits, double d)
        {
            double result = 0.0;
            if (double.TryParse(d.ToString(), out result))
            {
                decimal b = new decimal(d);
                double formatD = Convert.ToDouble(Math.Round(b, digits));
                //Double formatD = b.setScale(digits, BigDecimal.ROUND_HALF_UP).doubleValue();
                return formatD;
            }
            else
            {
                return 0.0;
            }
        }
    }


}
